unit DrawOpScheme;

interface

uses OpSchemes, DrawObject, StdDrawObjects, Graphics;
type
  TDrawOpScheme = class(TDrawObject)
  public
    constructor Create(OpScheme: TOpScheme);
  end;

  TDrawOSNode = class(TDrawObject)
  private
    FNode: TOSNode;
    function GetNodeHeight: Double;
    function GetNodeWidth: Double;
  protected
    procedure InternalDraw; override;
    function GetMinX: Double; override;
    function GetMinY: Double; override;
    function GetMaxX: Double; override;
    function GetMaxY: Double; override;
    function InternalTestHit(X, Y: Double): Boolean; override;
    procedure InternalDrop(DX, DY: Double); override;

  public
    property NodeWidth: Double read GetNodeWidth;
    property NodeHeight: Double read GetNodeHeight;
    constructor Create(OSNode: TOSNode);
  end;

implementation
uses Math, Types, BourbakiLogic;
const
  OSNodeWidth = 110;
  OSNodeHeight = 50;
  OSHMargin = 5;

type
  TDrawOSEdge = class(TDrawObject)
  private
    FNodeFrom, FNodeTo: TDrawOSNode;
  protected
    procedure InternalDraw; override;
  public
    constructor Create(NodeFrom, NodeTo: TDrawOSNode);
  end;
  { TDrawOpScheme }

constructor TDrawOpScheme.Create(OpScheme: TOpScheme);
  function GetNodeIndex(Node: TOSNode): Integer;
  var
    i: Integer;
  begin
    Result := -1;
    for i := 0 to Pred(OpScheme.NodeCount) do
      if OpScheme.Node[i] = Node then
      begin
        Result := i;
        Break;
      end;
  end;
var
  i: Integer;
  DrawNodes: array of TDrawOSNode;
begin
  SetLength(DrawNodes, OpScheme.NodeCount);
  for i := 0 to Pred(OpScheme.NodeCount) do
  begin
    DrawNodes[i] := TDrawOSNode.Create(OpScheme.Node[i]);
    AddToQueue(DrawNodes[i]);
  end;

  for i := 0 to Pred(OpScheme.EdgeCount) do
  begin
    AddToQueue(TDrawOSEdge.Create(
      DrawNodes[GetNodeIndex(OpScheme.Edge[i].SubNode)],
      DrawNodes[GetNodeIndex(OpScheme.Edge[i].Node)]));
    AddToQueue(TDrawLabel.Create(OpScheme.Edge[i].Label_));
  end;
end;

{ TDrawOSNode }

constructor TDrawOSNode.Create(OSNode: TOSNode);
begin
  FNode := OSNode;
end;

function TDrawOSNode.GetMaxX: Double;
begin
  Result := FNode.X + NodeWidth / 2;
end;

function TDrawOSNode.GetMaxY: Double;
begin
  Result := FNode.Y + NodeHeight / 2;
end;

function TDrawOSNode.GetMinX: Double;
begin
  Result := FNode.X - NodeWidth / 2;
end;

function TDrawOSNode.GetMinY: Double;
begin
  Result := FNode.Y - NodeHeight / 2;
end;

function TDrawOSNode.GetNodeHeight: Double;
begin
  Result := OSNodeHeight;
end;

function TDrawOSNode.GetNodeWidth: Double;
begin
  Result := OSNodeWidth;
end;

procedure TDrawOSNode.InternalDraw;
var
  //  TextWidth, TextHeight: Integer;
  NodeRect: TRect;
begin
  //  TextWidth := Canvas.TextWidth(FNode.Caption);
  //  TextHeight := Canvas.TextHeight(FNode.Caption);
  Canvas.Font.Height := Stretch(Canvas.Font.Height, dNone);
  NodeRect := Rect(MinX, MinY, MaxX, MaxY);
  Canvas.TextRect(NodeRect, MinX + Stretch(OSHMargin, dNone),
    MinY + Stretch(OSHMargin, dNone), FNode.Scheme.Caption);
  Canvas.Brush.Style := bsClear;
  Canvas.Rectangle(NodeRect);
end;

procedure TDrawOSNode.InternalDrop(DX, DY: Double);
begin
  FNode.X := FNode.X + DX;
  FNode.Y := FNode.Y + DY;
end;

function TDrawOSNode.InternalTestHit(X, Y: Double): Boolean;
begin
  Result := (X > FNode.X - NodeWidth /2)
    and (X < FNode.X + NodeWidth /2)
    and (Y > FNode.Y - NodeHeight /2)
    and (Y < FNode.Y + NodeHeight /2)
end;

{ TDrawOSEdge }

constructor TDrawOSEdge.Create(NodeFrom, NodeTo: TDrawOSNode);
begin
  FNodeFrom := NodeFrom;
  FNodeTo := NodeTo;
end;

procedure TDrawOSEdge.InternalDraw;
var
  DX, DY: Double;
  DXFrom, DXTo, DYFrom, DYTo: Double;
begin
  DX := FNodeTo.FNode.X - FNodeFrom.FNode.X;
  DY := FNodeFrom.FNode.Y - FNodeTo.FNode.Y;

  if DY = 0 then
  begin
    DXFrom := FNodeFrom.NodeWidth;
    DXTo := FNodeTo.NodeWidth;
  end
  else
  begin
    DXFrom := Min(FNodeFrom.NodeHeight * Abs(DX),
      FNodeFrom.NodeWidth * Abs(DY)) / Abs(DY);
    DXTo := Min(FNodeTo.NodeHeight * Abs(DX),
      FNodeTo.NodeWidth * Abs(DY)) / Abs(DY);
  end;
  DXFrom := Sign(DX) * DXFrom / 2;
  DXTo := -Sign(DX) * DXTo / 2;

  if DX = 0 then
  begin
    DYFrom := FNodeFrom.NodeHeight;
    DYTo := FNodeTo.NodeHeight;
  end
  else
  begin
    DYFrom := Min(FNodeFrom.NodeWidth * Abs(DY),
      FNodeFrom.NodeHeight * Abs(DX)) / Abs(DX);
    DYTo := Min(FNodeTo.NodeWidth * Abs(DY),
      FNodeTo.NodeHeight * Abs(DX)) / Abs(DX);
  end;
  DYFrom := -Sign(DY) * DYFrom / 2;
  DYTo := Sign(DY) * DYTo / 2;

  DrawArrow(DXFrom + FNodeFrom.FNode.X,
    DYFrom + FNodeFrom.FNode.Y,
    DXTo + FNodeTo.FNode.X,
    DYTo + FNodeTo.FNode.Y, atArrowSmall);
end;

end.

